﻿#ifndef ___PCLIB_S_JSON___
#define ___PCLIB_S_JSON___


#include "../m_core/m_core.hpp"

#include "rapidjson-1.1.0/rapidjson.h"
#include "rapidjson-1.1.0/document.h"
#include "rapidjson-1.1.0/pointer.h"
#include "rapidjson-1.1.0/writer.h"
#include "rapidjson-1.1.0/prettywriter.h"
#include "rapidjson-1.1.0/stringbuffer.h"
#include "rapidjson-1.1.0/error/error.h"
#include "rapidjson-1.1.0/error/en.h"
#include "rapidjson-1.1.0/filereadstream.h"
#include "rapidjson-1.1.0/filewritestream.h"
#include "rapidjson-1.1.0/encodings.h"
#include "rapidjson-1.1.0/stream.h"


/**
*@brief  json对象解析[DOM风格，将整个json字符串在内存中构建成对象。接口简单易用，但不适合操作巨大的json数据] 
*        在读写json元素时，使用类似xpath的语法进行访问
*        被称为Json Pointer 见 https://tools.ietf.org/html/rfc6901
*        json pointer路径，示例[!注意：数组下标从0开始]：
*          /key1           可以访问value1 : {"key1":"value1"}
*          /key1/key12     可以访问value12: {"key1":{"key12":"value12"} }
*          /key1/key12/1   可以访问value2 : {"key1":{"key12":["value1","value2","value3"]} }
*          /key1/key12/-   可以访问value4(特殊的-号代表访问数组的最后，通常用于给数组添加元素) 
*          空串或NULL       可以访问整个json串
*@note  性能分析可参考：https://zhuanlan.zhihu.com/p/22399604
*@note  使用流程：
* CPCJsonObject json;                                       //创建:json对象
* auto result   = json.Parse("{\"strkey\":\"strvalue\"}");  //解析:使用json对象解析json字符串
* auto strvalue = json.Get("/strkey","defalutvalue");       //读:字符串值，失败返回默认值(默认值类型即你要获取的类型)。
* auto intvalue = json.Get("/intkey",0);                    //读:其他类型值，如果你关心错误原因，可调用LastErrorInfo()
* json.Set("/strkey","newvalue");                           //写:字符串值
* json.Set("/intkey",111);                                  //写:其他类型。写入值可基于一个空json，也可基于已经解析好的
* ret = json.Stringify();                                   //转换:将json对象再转换为json字符串
*/
class CPCJsonObject 
{
public:
    /**
    *@brief   空json
    */
    explicit CPCJsonObject(bool bSrictMode = true)
        :m_bSrictMode(bSrictMode)
    {
        InitObject();
    }
    ~CPCJsonObject()
    {}

    /**
    *@brief   初始化并清空对象为空json对象
    */
    void InitObject()
    { 
        m_doc.SetObject();
    }

    /**
    *@brief   初始化并清空对象为空json数组
    */
    void InitArray()
    {
        m_doc.SetArray();
    }

    //解析和生成
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /**
    *@brief     从json字符串加载
    *@param     str[in]    json字符串
    *@return    是否成功
    */
    CPCResult<> Parse(const char* str)
    {
        CPCResult<> result;
        if(NULL == str || str[0] == 0)
        {
            return result.SetFail(-1, "input str is empty.");
        }
        this->InitObject();
        if (m_doc.Parse(str).HasParseError())
        {
            return result.SetFail((int)m_doc.GetErrorOffset(),"parse json fail. cause:[%s]. error offset: [%s]",
                rapidjson::GetParseError_En(m_doc.GetParseError()),
                CPCStrUtil::SizeToString(m_doc.GetErrorOffset()).c_str());
        }
        return result;
    }

    /**
    *@brief     从json文件加载，会自动智能处理BOM
    *@param     path[in]    文件路径
    *@return    是否成功
    *@note      注意，如果文件是unicode，会将其解析到utf-8的DOM
    */
    CPCResult<> ParseFile(const char* path)
    {
        CPCResult<> result;
        if(NULL == path || path[0] == 0)
        {
            return result.SetFail("path is empty.");
        }
#if defined(PCOS_WIN)
        FILE* fp = fopen(path, "rb"); 
#else
        FILE* fp = fopen(path, "r"); 
#endif
        if(NULL == fp)
        {
            return result.SetSystemFail();
        }
        CPCAutoObj autoClose([&fp](){fclose(fp);});
        
        //自动处理Encoding，这里不使用AutoUTF是因为它不能正确处理gbk文件
        auto result2 = CPCFileUtil::PCSkipBOM(fp);
        char readBuffer[1024];
        rapidjson::FileReadStream is(fp, readBuffer, sizeof(readBuffer));
        bool bParsedFail = true;
        if(result2.Get() == PC_TEXT_UCS2_LE_BOM)
        {
            //ucs2-le 用eis包装bis
            rapidjson::EncodedInputStream<rapidjson::UTF16LE<>, rapidjson::FileReadStream> eis(is);  
            this->InitObject();
            bParsedFail = m_doc.ParseStream<0,rapidjson::UTF16LE<>>(eis).HasParseError();
        }
        else if(result2.Get() == PC_TEXT_UCS2_BE_BOM)
        {
            //ucs2-be 用eis包装bis
            rapidjson::EncodedInputStream<rapidjson::UTF16BE<>, rapidjson::FileReadStream> eis(is);  
            this->InitObject();
            bParsedFail = m_doc.ParseStream<0,rapidjson::UTF16BE<>>(eis).HasParseError();
        }
        else
        {
            bParsedFail = m_doc.ParseStream(is).HasParseError();
        }
        
        if (bParsedFail)
        {
            return result.SetFail("parse json fail. cause:[%s]. error offset: [%s]",
                rapidjson::GetParseError_En(m_doc.GetParseError()),
                CPCStrUtil::SizeToString(m_doc.GetErrorOffset()).c_str());
        }
        return result;
    }

    /**
    *@brief     输出json字符串
    *@param     bPretty[in]  是否格式化输出
    *@param     key[in]      对应的节点，比如可以将"/body"节点对应的value输出为字符串。如果未指定，默认输出整个json串。
    *@return    是否成功，成功则结果在 std::string
    */
    CPCResult<std::string> Stringify(bool bPretty = false, const char* key = NULL)
    {
        CPCResult<std::string> result;
        bool bSucc = false;
        rapidjson::StringBuffer buffer;
        if(bPretty)
        {
            rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(buffer);
            if(key != NULL && key[0] != 0)
            {
                rapidjson::Value* value = (*this)[key];
                if(value == NULL)
                {
                    std::string tmp(std::move(m_lastErrInfo));
                    return result.SetFail("%s",tmp.c_str());
                }
                bSucc = value->Accept(writer);
            }
            else
            {
                bSucc = m_doc.Accept(writer);
            }
        }
        else
        {
            rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
            if(key != NULL && key[0] != 0)
            {
                rapidjson::Value* value = (*this)[key];
                if(value == NULL)
                {
                    std::string tmp(std::move(m_lastErrInfo));
                    return result.SetFail("%s",tmp.c_str());
                }
                bSucc = value->Accept(writer);
            }
            else
            {
                bSucc = m_doc.Accept(writer);
            }
        }
        if(!bSucc)
        {
            return result.SetFail("Writer json fail.");
        }
        return result.SetSucc(std::string(buffer.GetString(), buffer.GetSize()));
    }

    /**
    *@brief     输出json文件
    *@param     path[in]     文件路径
    *@param     bPretty[in]  是否格式化输出
    *@param     key[in]      对应的节点，比如可以将"/body"节点对应的value输出为字符串。如果未指定，默认输出整个json串。
    *@return    是否成功
    */
    CPCResult<> StringifyFile(const char* path, bool bPretty = false, const char* key = NULL)
    {
        CPCResult<> result;
        auto result2 = this->Stringify(bPretty,key);
        if(!result2)
        {
            return result.SetFail("Stringify fail:%s",result2.ErrDesc());
        }
        auto& json = result2.Get();
        return CPCFileUtil::PCWriteFile(path,json.data(),json.length());
    }

    //读json
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    
    /**
    *@brief     如果你关心读写失败的错误原因，可以调用此函数 判断是否发生了读写值时的错误
    *@return    最后的读写时是否发生了错误。
    *@note      只有读写出错才需要调用
    */
    bool HasError() const
    {
        return !m_lastErrInfo.empty();
    }

    /**
    *@brief     如果你关心读写失败的错误原因，可以调用此函数 获取最后的错误原因
    *@return    最后的读写错误。如果为空代表无错误
    *@note      只有读写出错才会写ErrInfo，请及时获取，因为错误信息会在下一次Get/Set时清除
    */
    const char* LastErrorInfo() const
    {
        return m_lastErrInfo.c_str();
    }

    /**
    *@brief     获取json对象的成员，有多个重载。你可以方便的获取需要的类型
    *@param     key[in]             json pointer路径
    *@param     defaultvalue[in]    默认值，出错时返回默认值。调用HasGetError/LastGetErrorInfo获取错误
    *@return    结果
    */
    const char* Get(const char* key, const char* defaultvalue)
    {
        rapidjson::Value* value = this->Get(key);
        if(value == NULL)
        {
            return defaultvalue;
        }
        if(!value->IsString())
        {
            m_lastErrInfo = CPCStrUtil::ShortFormat(" key : %s  value is not string [is %s]", key, JSONValueGetTypeName(value));
            return defaultvalue;
        }
        return value->GetString();
    }
    std::string Get(const char* key, std::string defaultvalue)
    {
        rapidjson::Value* value = this->Get(key);
        if(value == NULL)
        {
            return defaultvalue;
        }
        if(!value->IsString())
        {
            m_lastErrInfo = CPCStrUtil::ShortFormat(" key : %s  value is not string [is %s]", key, JSONValueGetTypeName(value));
            return defaultvalue;
        }
        return std::string(value->GetString(),value->GetStringLength());
    }
    int Get(const char* key, int defaultvalue)
    {
        rapidjson::Value* value = this->Get(key);
        if(value == NULL)
        {
            return defaultvalue;
        }
        if(!value->IsInt())
        {
            m_lastErrInfo = CPCStrUtil::ShortFormat(" key : %s  value is not int [is %s]", key, JSONValueGetTypeName(value));
            return defaultvalue;
        }
        return value->GetInt();
    }
    unsigned Get(const char* key, unsigned defaultvalue)
    {
        rapidjson::Value* value = this->Get(key);
        if(value == NULL)
        {
            return defaultvalue;
        }
        if(!value->IsUint())
        {
            m_lastErrInfo = CPCStrUtil::ShortFormat(" key : %s  value is not unsigned int [is %s]", key, JSONValueGetTypeName(value));
            return defaultvalue;
        }
        return value->GetUint();
    }
    int64_t Get(const char* key, int64_t defaultvalue)
    {
        rapidjson::Value* value = this->Get(key);
        if(value == NULL)
        {
            return defaultvalue;
        }
        if(!value->IsInt64())
        {
            m_lastErrInfo = CPCStrUtil::ShortFormat(" key : %s  value is not int64 [is %s]", key, JSONValueGetTypeName(value));
            return defaultvalue;
        }
        return value->GetInt64();
    }
    uint64_t Get(const char* key, uint64_t defaultvalue)
    {
        rapidjson::Value* value = this->Get(key);
        if(value == NULL)
        {
            return defaultvalue;
        }
        if(!value->IsUint64())
        {
            m_lastErrInfo = CPCStrUtil::ShortFormat(" key : %s  value is not unsigned int64 [is %s]", key, JSONValueGetTypeName(value));
            return defaultvalue;
        }
        return value->GetUint64();
    }
    bool Get(const char* key, bool defaultvalue)
    {
        rapidjson::Value* value = this->Get(key);
        if(value == NULL)
        {
            return defaultvalue;
        }
        if(!value->IsBool())
        {
            m_lastErrInfo = CPCStrUtil::ShortFormat(" key : %s  value is not bool  [is %s]", key, JSONValueGetTypeName(value));
            return defaultvalue;
        }
        return value->GetBool();
    }
    float Get(const char* key, float defaultvalue)
    {
        rapidjson::Value* value = this->Get(key);
        if(value == NULL)
        {
            return defaultvalue;
        }
        if(!value->IsFloat())
        {
            m_lastErrInfo = CPCStrUtil::ShortFormat(" key : %s  value is not float  [is %s]", key, JSONValueGetTypeName(value));
            return defaultvalue;
        }
        return value->GetFloat();
    }
    double Get(const char* key, double defaultvalue)
    {
        rapidjson::Value* value = this->Get(key);
        if(value == NULL)
        {
            return defaultvalue;
        }
        if(!value->IsDouble())
        {
            m_lastErrInfo = CPCStrUtil::ShortFormat(" key : %s  value is not double  [is %s]", key, JSONValueGetTypeName(value));
            return defaultvalue;
        }
        return value->GetDouble();
    }
    rapidjson::Value* Get(const char* key)
    {
        m_lastErrInfo.clear();
        rapidjson::Pointer pointer = GetRealPointer(key);
        if(!pointer.IsValid())
        {
            m_lastErrInfo = CPCStrUtil::ShortFormat("key[%s] is invalid. cause:[%d]. error offset: [%s]",
                key, (int)pointer.GetParseErrorCode(), CPCStrUtil::SizeToString(pointer.GetParseErrorOffset()).c_str());
            return NULL;
        }
        rapidjson::Value*  value = pointer.Get(m_doc);
        if(nullptr == value)
        {
            m_lastErrInfo = CPCStrUtil::ShortFormat("not find key : [%s]", key);
            return NULL;
        }
        return value;
    }

    /**
    *@brief     判断对应的值是否为null
    *@param     key[in]    json pointer路径
    *@return    是否为null，只有明确不为null才会返回false
    */
    bool GetNull(const char* key)
    {
        rapidjson::Value* value = this->Get(key);
        if(value == NULL)
        {
            return true;
        }
        return (value->IsNull());
    }

    //写json
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /**
    *@brief     设置json对象的成员，如果是设置为null，要调用SetNull
    *@param     key[in]     json pointer路径
    *@param     value[in]   value
    *@param     bCover[in]  是否覆盖原来的值
    *@return    是否成功，如果你确定key没传错，一般不会失败
    */
    template<typename T>
    bool Set(const char* key, T value, bool bCover = true)
    {
        m_lastErrInfo.clear();
        rapidjson::Pointer pointer = GetRealPointer(key);
        if(!pointer.IsValid())
        {
            m_lastErrInfo = "not find key";
            return false;
        }
        if(bCover || NULL == pointer.Get(m_doc))
        {
            pointer.Set(m_doc, value);
        }
        return true;
    }

    /**
    *@brief     设置json对象的成员为null
    *@param     key[in]     json pointer路径
    *@param     bCover[in]  是否覆盖原来的值
    *@return    是否成功，如果你确定key没传错，一般不会失败
    */
    bool SetNull(const char* key, bool bCover = true)
    {
        m_lastErrInfo.clear();
        rapidjson::Pointer pointer = GetRealPointer(key);
        if (!pointer.IsValid())
        {
            m_lastErrInfo = "not find key";
            return false;
        }
        if (bCover || NULL == pointer.Get(m_doc))
        {
            pointer.Set(m_doc,rapidjson::kNullType);
        }
        return true;
    }

    /**
    *@brief     设置json对象的成员
    *@param     key[in]     json pointer路径
    *@param     value[in]   value，指向某个值的指针，性能警告：这里value会深拷贝进去
    *@param     bCover[in]  是否覆盖原来的值
    *@return    是否成功，如果你确定key没传错，一般不会失败
    */
    bool SetRapidJsonValue(const char* key, rapidjson::Value* value, bool bCover = true)
    {
        m_lastErrInfo.clear();
        rapidjson::Pointer pointer = GetRealPointer(key);
        if(!pointer.IsValid())
        {
            m_lastErrInfo = "not find key";
            return false;
        }
        if(bCover || NULL == pointer.Get(m_doc))
        {
            if(value == NULL)
            {
                pointer.Set(m_doc,rapidjson::kNullType);
            }
            else
            {
                rapidjson::Value tmpValue(*value, m_doc.GetAllocator());
                pointer.Set(m_doc, tmpValue.Move(), m_doc.GetAllocator());
            }
        }
        return true;
    }

    /**
    *@brief     移除json对象的成员
    *@param     key[in]    json pointer路径
    *@return    是否成功，如果你确定key没传错，一般不会失败
    */
    bool SetErase(const char* key)
    {
        m_lastErrInfo.clear();
        rapidjson::Pointer pointer = GetRealPointer(key);
        if(!pointer.IsValid())
        {
            m_lastErrInfo = "not find key";
            return false;
        }
        if(!pointer.Erase(m_doc))
        {
            m_lastErrInfo = "erase this key fail.";
            return false;
        }
        return true;
    }

    /**
    *@brief     设置json对象的成员数组
    *@param     key[in]    json pointer路径
    *@return    是否成功，如果你确定key没传错，一般不会失败
    */
    bool SetArray(const char* key)
    {
        m_lastErrInfo.clear();
        rapidjson::Pointer pointer = GetRealPointer(key);
        if(!pointer.IsValid())
        {
            m_lastErrInfo = "not find key";
            return false;
        }
        pointer.Set(m_doc,rapidjson::kArrayType);
        return true;
    }

    /**
    *@brief     设置json对象的成员为对象
    *@param     key[in]    json pointer路径
    *@return    是否成功，如果你确定key没传错，一般不会失败
    */
    bool SetObject(const char* key)
    {
        m_lastErrInfo.clear();
        rapidjson::Pointer pointer = GetRealPointer(key);
        if(!pointer.IsValid())
        {
            m_lastErrInfo = "not find key";
            return false;
        }
        pointer.Set(m_doc,rapidjson::kObjectType);
        return true;
    }

public:
    /**
    *@brief     获取元素
    *@param     key[in]    json pointer路径
    *@return    元素指针。失败为空
    *@note      如果你需要知道错误原因，调用LastGetErrorInfo()
    */
    rapidjson::Value* operator[](const char* key)
    {
        return this->Get(key);
    }

    /**
    *@brief     获取json类型字符串
    *@param     type[in]    类型枚举
    *@return    类型字符串。
    */
    const char* JSONGetTypeName(rapidjson::Type type) const
    {
        static const char* kTypeNames[]  = { "Null", "False", "True", "Object", "Array", "String", "Number" };
        if((int)type < 0 || (int)type > 6)
        {
            return "unKnown";
        }
        return kTypeNames[(int)type];
    }

    /**
    *@brief     获取json类型字符串
    *@param     value[in]    json值
    *@return    类型字符串。
    */
    const char* JSONValueGetTypeName(rapidjson::Value* value) const
    {
        if(value == NULL) return "unKnown";
        return JSONGetTypeName(value->GetType());
    }

    /**
    *@brief     严格模式和宽松模式的选择。
    *           宽松模式第一个字符可以不用加/（但是会损失性能）
    */
    rapidjson::Pointer GetRealPointer(const char* key)
    {
        if (key == NULL || key[0] == 0)
        {
            return rapidjson::Pointer("");
        }
        if (m_bSrictMode)
        {
            return rapidjson::Pointer(key);
        }
        
        if (key[0] == '/' || key[0] == '#')
        {
            return rapidjson::Pointer(key);
        }
        else
        {
            std::string realkey = "/";
            realkey += key;
            return rapidjson::Pointer(realkey.c_str());
        }
    }

public:
    rapidjson::Document m_doc; 
    std::string m_lastErrInfo;
    bool        m_bSrictMode;
};




#endif    //___PCLIB_S_JSON___
